﻿//*************************************************************************************************
// DataBindingHelper.cs
// Owner: Mahipal Kante
//
// Data Binding helper class
//
// Copyright(c) Microsoft Corporation, 2010
//*************************************************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.Globalization;

namespace WebTest.WebService.Plugin.Runtime
{
    internal class DataBindingHelper
    {
        // ************************************************************************
        // Updating the binding sites goes through the string and replaces all 
        // sites with the appropriate value found in the context's property bag.
        // ************************************************************************
        internal static string UpdateBindingSites(WebTestContext testCaseContext, string preBoundString)
        {
            string bindingSite;
            int startingIndex = 0;
            int bindingSiteIndex;
            int nextStartingIndex = 0;
            StringBuilder postBoundBuilder = null;

            // Scan the entire length of the string 
            if (!String.IsNullOrEmpty(preBoundString))
            {
                while ((bindingSite = GetNextBindingSite(preBoundString, startingIndex, out bindingSiteIndex, out nextStartingIndex)) != null)
                {
                    //lazy create the StringBuilder, since many fields will not be context-bound
                    if (postBoundBuilder == null)
                    {
                        postBoundBuilder = new StringBuilder();
                    }

                    // First copy any data up to the start of the binding site
                    postBoundBuilder.Append(preBoundString.Substring(startingIndex, bindingSiteIndex - startingIndex));

                    //advance the starting index to be ready for the next pass
                    startingIndex = nextStartingIndex;

                    string boundValue = null;

                    if (bindingSite.StartsWith("$", StringComparison.Ordinal) && !bindingSite.StartsWith("$HIDDEN", StringComparison.Ordinal))
                    {
                        boundValue = GetBindingMacroValue(testCaseContext, bindingSite);
                    }

                    if (boundValue == null)
                    {
                        // Now add the value for this binding site to the string
                        if (!testCaseContext.ContainsKey(bindingSite))
                        {
                            //string message = String.Format(CultureInfo.InvariantCulture, Strings.Get(Strings.BindingSiteException), bindingSite);
                            throw new WebTestException("Data Not Found for:" + bindingSite);
                        }

                        boundValue = testCaseContext[bindingSite] as string;
                        if (boundValue == null)
                        {
                            // call ToString() if the value was not a string
                            boundValue = testCaseContext[bindingSite].ToString();
                        }
                    }

                    postBoundBuilder.Append(boundValue);
                }

                //if we actually did any binding, add the rest of the string to the string builder and return the bound string
                if (postBoundBuilder != null)
                {
                    postBoundBuilder.Append(preBoundString.Substring(nextStartingIndex));
                    return postBoundBuilder.ToString();
                }
            }

            //no binding occurred, so return the original string
            return preBoundString;
        }

        /// <summary>
        /// Returns the next name of a binding site -- without {{ and }}
        /// </summary>
        /// <param name="searchString">The string to search for binding sites.</param>
        /// <param name="startIndex">The index at which to start looking in the string.</param>
        /// <param name="startBindingIndex">The index of the first { character in the binding site.  Behavior is undefined if no binding site was found.</param>
        /// <param name="endIndex">The index at which parsing stopped.  This should be used as the next starting index.</param>
        /// <returns></returns>
        private static string GetNextBindingSite(string searchString, int startIndex, out int startBindingIndex, out int endIndex)
        {
            endIndex = startIndex;
            startBindingIndex = searchString.IndexOf("{{", startIndex, StringComparison.Ordinal);

            if (startBindingIndex == -1)
            {
                //no binding was found
                return null;
            }

            if (searchString.Length < startBindingIndex + 4)
            {
                //this is an error because the string is not long enough for the binding to be closed
                return null;
            }

            int endBindingIndex = searchString.IndexOf("}}", startBindingIndex + 2, StringComparison.Ordinal);

            if (endBindingIndex == -1)
            {
                //this is an error because the binding is not closed
                return null;
            }

            endIndex = endBindingIndex + 2;

            return searchString.Substring(startBindingIndex + 2, endBindingIndex - startBindingIndex - 2);
        }

        private static string GetBindingMacroValue(WebTestContext testCaseContext, string bindingSite)
        {
            if (string.Equals(bindingSite, "$AgentCount", StringComparison.OrdinalIgnoreCase))
            {
                return testCaseContext.AgentCount.ToString(CultureInfo.CurrentCulture);
            }

            if (string.Equals(bindingSite, "$AgentId", StringComparison.OrdinalIgnoreCase))
            {
                return testCaseContext.AgentId.ToString(CultureInfo.CurrentCulture);
            }

            if (string.Equals(bindingSite, "$AgentName", StringComparison.OrdinalIgnoreCase))
            {
                return testCaseContext.AgentName;
            }

            if (string.Equals(bindingSite, "$ControllerName", StringComparison.OrdinalIgnoreCase))
            {
                return testCaseContext.ControllerName;
            }

            if (string.Equals(bindingSite, "$WebTestUserId", StringComparison.OrdinalIgnoreCase))
            {
                return testCaseContext.WebTestUserId.ToString(CultureInfo.CurrentCulture);
            }

            if (string.Equals(bindingSite, "$WebTestIteration", StringComparison.OrdinalIgnoreCase))
            {
                return testCaseContext.WebTestIteration.ToString(CultureInfo.CurrentCulture);
            }

            const string randomIntMacroPrefix = "$RandomInt(";
            if (bindingSite.StartsWith(randomIntMacroPrefix, StringComparison.OrdinalIgnoreCase) &&
                bindingSite.EndsWith(")", StringComparison.Ordinal))
            {
                Random rand = new Random();
                string arguments = bindingSite.Substring(randomIntMacroPrefix.Length, bindingSite.Length - randomIntMacroPrefix.Length - 1);
                if (arguments.Length == 0)
                {
                    return rand.Next().ToString(CultureInfo.CurrentCulture);
                }
                else if (arguments.Contains(","))
                {
                    string[] args = arguments.Split(new char[] { ',' });
                    if (args.Length == 2)
                    {
                        try
                        {
                            return rand.Next(
                                int.Parse(args[0], CultureInfo.InvariantCulture),
                                int.Parse(args[1], CultureInfo.InvariantCulture)).ToString(CultureInfo.CurrentCulture);
                        }
                        catch
                        {
                        }
                    }
                }
                else
                {
                    try
                    {
                        return rand.Next(int.Parse(arguments, CultureInfo.InvariantCulture)).ToString(CultureInfo.CurrentCulture);
                    }
                    catch
                    {
                    }
                }
            }

            const string randomDoubleMacroPrefix = "$RandomDouble(";
            if (bindingSite.StartsWith(randomDoubleMacroPrefix, StringComparison.OrdinalIgnoreCase) &&
                bindingSite.EndsWith(")", StringComparison.Ordinal))
            {
                Random rand = new Random();
                string arguments = bindingSite.Substring(randomDoubleMacroPrefix.Length, bindingSite.Length - randomDoubleMacroPrefix.Length - 1);
                if (arguments.Length == 0)
                {
                    return rand.NextDouble().ToString(CultureInfo.CurrentCulture);
                }
                else if (arguments.Contains(","))
                {
                    string[] args = arguments.Split(new char[] { ',' });
                    if (args.Length == 2)
                    {
                        try
                        {
                            double min = double.Parse(args[0], CultureInfo.InvariantCulture);
                            double max = double.Parse(args[1], CultureInfo.InvariantCulture);
                            double range = max - min;

                            double randValue = min + (range * rand.NextDouble());
                            return randValue.ToString(CultureInfo.CurrentCulture);
                        }
                        catch
                        {
                        }
                    }
                }
                else
                {
                    try
                    {
                        double randValue = double.Parse(arguments, CultureInfo.InvariantCulture) * rand.NextDouble();
                        return randValue.ToString(CultureInfo.CurrentCulture);
                    }
                    catch
                    {
                    }
                }
            }

            return null;
        }
    }
}
